package org.apache.lucene.index;

/**
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.lucene.util.PriorityQueue;

/**
 * Describe class <code>MultipleTermPositions</code> here.
 * 
 * @author Anders Nielsen
 * @version 1.0
 */
public class MultipleTermPositions implements TermPositions {

    private static final class TermPositionsQueue extends PriorityQueue {
        TermPositionsQueue(List termPositions) throws IOException {
            initialize(termPositions.size());

            Iterator i = termPositions.iterator();
            while (i.hasNext()) {
                TermPositions tp = (TermPositions) i.next();
                if (tp.next())
                    put(tp);
            }
        }

        final TermPositions peek() {
            return (TermPositions) top();
        }

        public final boolean lessThan(Object a, Object b) {
            return ((TermPositions) a).doc() < ((TermPositions) b).doc();
        }
    }

    private static final class IntQueue {
        private int _arraySize = 16;
        private int _index = 0;
        private int _lastIndex = 0;
        private int[] _array = new int[_arraySize];

        final void add(int i) {
            if (_lastIndex == _arraySize)
                growArray();

            _array[_lastIndex++] = i;
        }

        final int next() {
            return _array[_index++];
        }

        final void sort() {
            Arrays.sort(_array, _index, _lastIndex);
        }

        final void clear() {
            _index = 0;
            _lastIndex = 0;
        }

        final int size() {
            return (_lastIndex - _index);
        }

        private void growArray() {
            int[] newArray = new int[_arraySize * 2];
            System.arraycopy(_array, 0, newArray, 0, _arraySize);
            _array = newArray;
            _arraySize *= 2;
        }
    }

    private int _doc;
    private int _freq;
    private TermPositionsQueue _termPositionsQueue;
    private IntQueue _posList;

    /**
     * Creates a new <code>MultipleTermPositions</code> instance.
     * 
     * @exception IOException
     */
    public MultipleTermPositions(IndexReader indexReader, Term[] terms) throws IOException {
        List termPositions = new LinkedList();

        for (int i = 0; i < terms.length; i++)
            termPositions.add(indexReader.termPositions(terms[i]));

        _termPositionsQueue = new TermPositionsQueue(termPositions);
        _posList = new IntQueue();
    }

    public final boolean next() throws IOException {
        if (_termPositionsQueue.size() == 0)
            return false;

        _posList.clear();
        _doc = _termPositionsQueue.peek().doc();

        TermPositions tp;
        do {
            tp = _termPositionsQueue.peek();

            for (int i = 0; i < tp.freq(); i++)
                _posList.add(tp.nextPosition());

            if (tp.next())
                _termPositionsQueue.adjustTop();
            else {
                _termPositionsQueue.pop();
                tp.close();
            }
        } while (_termPositionsQueue.size() > 0 && _termPositionsQueue.peek().doc() == _doc);

        _posList.sort();
        _freq = _posList.size();

        return true;
    }

    public final int nextPosition() {
        return _posList.next();
    }

    public final boolean skipTo(int target) throws IOException {
        while (_termPositionsQueue.peek() != null && target > _termPositionsQueue.peek().doc()) {
            TermPositions tp = (TermPositions) _termPositionsQueue.pop();
            if (tp.skipTo(target))
                _termPositionsQueue.put(tp);
            else
                tp.close();
        }
        return next();
    }

    public final int doc() {
        return _doc;
    }

    public final int freq() {
        return _freq;
    }

    public final void close() throws IOException {
        while (_termPositionsQueue.size() > 0)
            ((TermPositions) _termPositionsQueue.pop()).close();
    }

    /**
     * Not implemented.
     * @throws UnsupportedOperationException
     */
    public void seek(Term arg0) throws IOException {
        throw new UnsupportedOperationException();
    }

    /**
     * Not implemented.
     * @throws UnsupportedOperationException
     */
    public void seek(TermEnum termEnum) throws IOException {
        throw new UnsupportedOperationException();
    }

    /**
     * Not implemented.
     * @throws UnsupportedOperationException
     */
    public int read(int[] arg0, int[] arg1) throws IOException {
        throw new UnsupportedOperationException();
    }

}
